/*file chiron src/modules.js */
/*preamble

    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    The license terms are stated in full in <license.rst> and at the end
    of all source files.

*/

/*status works, passes tests in Firefox, Safari, and Explorer 6 */
/*quality .8 */
/*stability .9 */
/*step 0 */
/*client-only */

/**

    Provides a module, `modules.js`, with the following features:

     - module loading
     - singleton modules (module object loaded and executed
       only the first time it's required).
     - module-relative module URL references

    A module is a JavaScript file.  `modules.js` uses an
    XHR (HTTP request) to get the file's text, the
    `eval` function to run the script, and `with` blocks
    to manage the scope chain in the script.

    These tools permit `modules.js` to load scripts
    only once and on demand, to completely avoid touching the
    global name space (no added members to the `window` object),
    to give every script its own private name space, and to insert
    names in a module's scope ("importing" using `include`).

    `modules.js` also masks its own existence from other scripts
    by removing it's ``<script>`` tag from the ``<head>``
    of the containing HTML document.  The module loader then
    detects the earliest possible moment after the document's
    DOM has fully loaded, presumably before the document has
    been rendered, then loads a list of ampersand delimited
    modules from the query `modules.js` query string::

        <script src="modules.js?includeMe.js"></script>

    A single page can have more than one self-contained
    module loader, with its own cache of modules.  Each
    module environment would be completely unaware of the
    others.

    `modules.js` "launders" the scope chain so that
    every module has a safe and clean execution environment.
    The scope chain in each module includes, from bottom
    to top:

     - the global object (`window`)
     - the module scope, which contains functions for
       loading other modules (`include`, `require`),
       convenience functiosn for logging to a cross-browser
       debuging consoles (`log`, `warn`, `error`),
       the `module`, and items loaded from other modules by
       `include`.
     - the `module` object.  Module scripts can export
       items by making them members of the `module`.
     - an empty closure (an "enclosure").  Variables
       declared with `var` inside a module script are only
       accessible inside the script.

    The context object, `this`, is the `module` inside
    a module script.

    `modules.js` also bootstraps these modules:

     - `modules.js` provides access to the module modulesjs
       internal functions and data.
     - `http.js` provides synchronous and synchronous, cross-platform
       XML HTTP request wrappers and URL parsing and formatting.
     - `environment.js` provides information about the Javascript
       execution environment discovered while bootstrapping
       the module modulesjs, like Windows/Mac/Linux,
       Browser/Rhino/Spidermonkey/Dashboard
       Firefox/Explorer/Safari/Opera.
     - `browser.js` provides cross platform element inspection
       rudiments.


*/

/*
    isolate the module modulesjs in an enclosure by creating an
    annonymous function and then--at the end of this file--
    calling it.

    also, take the evalGlobal function as an argument so that it can be
    declared in global scope.  this prevents the eval function
    from inheriting variables from the modulesjs enclosure scope.
*/
(function (evalGlobal) { /* enclosure */

/*
    chicken and the egg.  modulesjs is a Module but also contains Module.
    Define Module and ModuleScope here first.  For a sensible
    order of documentation, we document modulesjs here, then 
    create it after Module and ModuleScope have been defined.
*/

/*** modules.js
    The modules system.  You can acquire a reference to the
    module system using a ``require("modules.js")`` 
    expression.  `modules.js` is the only module that cannot
    be included in another module; you can only use `require`.
*/

/**** beginTime
    the `Date` that the module loader began building itself.
*/
var beginTime = new Date();

/**** endTime
    the `Date` that the module loader finished building
    itself and began waiting for the DOM to load.
*/
/* this value is set toward the bottom of this script */

/**** readyTime
    the `Date` that the module loader deemed that it was
    ready to begin requiring modules.  This is the earliest
    time that the script can detect that the DOM has been
    fully loaded.
*/
/* this value is set in the `ready` function toward the
 * bottom of this script. */

/**** Module
    a function that returns a new, empty, module object.
*/

var Module = function (url, module) {
    if (module === undefined)
        module = {};

    /***** moduleScope
        a `ModuleScope` object that contains names that are
        available inside a module, like logging functions, module
        functions, and functions included in the module's scope
        but are not exported to other modules.  Each module's
        `include` function writes names from other modules
        into `moduleScope`, not the `module` object
        itself.
    */
    module.moduleScope = new ModuleScope(module, url);

    return module;
};

/**** ModuleScope
    a native JavaScript type for module scopes.  All the names
    in a module scope are available to its module, but they are
    private in the sense that including a module in another module
    does not copy the former module's scope.
*/

var ModuleScope = function (module, url) {
    var self = this;

    /***** globals
        a reference to global scope.  When using `modules.js` in a client,
        ``global`` is the ``window``.  This value exists to provide a consistent
        interface to the ``global`` object for scripts that can be executed
        in other environments such as Rhino, particularly XJS.
    */
    self.globals = window;

    /***** locals
        a reference to the private module scope (the module scope itself)
    */
    self.locals = self;

    /***** module
        a synonym reference for module that contains this `ModuleScope`.
        `module` is a handy name for the current module object when ``this``
        will not suffice, like in a method.
    */
    self.module = module;

    /***** module
        a synonym reference for module that contains this `ModuleScope`.
    */
    self.exports = module;

    /***** moduleUrl
        the URL of the JavaScript file that produced the
        corresponding module.
    */
    self.moduleUrl = url;

    /***** modulesUrl
        the fully qualified path URL leading up to ``modules.js``
        in the HTML script tag hyper-reference.
    */
    self.modulesUrl = modulesjsUrl;

    /***** moduleBind
        moduleBind is a dual purpose function that defers
        member selection and binding to the module to
        call time.  used to bind moduleScope functions
        to the module and by include to bind functions
        decorated with foreignModuleBind (like base.js's
        type, dir, and vars) to the module in which they're
        included rather than the module in which they're
        declared.
    */
    var moduleBind = self.moduleBind = function (items, key) {
        return function () {
            return items[key].apply(module, arguments);
        };
    };

    /**
        module scopes contain special bound copies of functions
        from the `modules.js` module and the `environment.js`
        module.  Functions with these names are available
        in the module scope of every module and are aware of the
        module that they were called from.  This permits them
        to perform behaviors like recording the URL of the module
        that called them or resolve URL arguments relative
        to the module that they are called in.
    */

    /* from modulesjs */

    /***** require
        a version of `require` from `modules.js` that is lazily bound
        (`lazy bind`) to this `ModuleScope`.
    */
    self.require = moduleBind(modulesjs, "require");

    /***** include
        a version of `include` from `modules.js` that is lazily bound
        (`lazy bind`) to this `ModuleScope`.
    */
    self.include = moduleBind(modulesjs, "include");

    /***** foreignModuleBind
        a version of `foreignModuleBind` from `modules.js` that is lazily bound
        (`lazy bind`) to this `ModuleScope`.
    */
    self.foreignModuleBind = moduleBind(modulesjs, "foreignModuleBind");

    /***** register
        a version of `register` from `modules.js` that is lazily bound
        (`lazy bind`) to this `ModuleScope`.
    */
    self.register = moduleBind(modulesjs, "register");

    /***** publish
        a version of `publish` from `modules.js` that is lazily bound
        (`lazy bind`) to this `ModuleScope`.
    */
    self.publish = moduleBind(modulesjs, "publish");

    /* from environment */

    /***** print
    */
    self.print = moduleBind(environment, "log");

    /***** log
        a version of `log` from `environment.js` (although defined in
        `modules.js`) that is lazily bound (`lazy bind`) to this `ModuleScope`.
    */
    self.log = moduleBind(environment, "log");

    /***** warn
        a version of `warn` from `environment.js` (although defined in
        `modules.js`) that is lazily bound (`lazy bind`) to this `ModuleScope`.
    */

    self.warn = moduleBind(environment, "warn");

    /***** error
        a version of `error` from `environment.js` (although defined in
        `modules.js`) that is lazily bound (`lazy bind`) to this `ModuleScope`.
    */
    self.error = moduleBind(environment, "error");

    /***** info
        a version of `info` from `environment.js` (although defined in
        `modules.js`) that is lazily bound (`lazy bind`) to this `ModuleScope`.
    */
    self.info = moduleBind(environment, "info");

};

/*
    The Deferred Declarations
    or: Egg and the Chicken
    =========================

    So, now we actually create the `modules.js`
    module object, then put the `Module` and
    `ModuleScope` declarations inside.  The documentation
    for these modules appears above, and the documentation
    for `module.js` immediately above that.
*/
var modulesjs = new Module("modules.js");
modulesjs.beginTime = beginTime;
modulesjs.Module = Module;
modulesjs.ModuleScope = ModuleScope;

/**** logging
    whether to log module load events.
*/
modulesjs.logging = false;

/**** logModule

    accepts:
     - a module URL `String`
     - an optional ``depth``, noting how many occurences
       of `require` closures are on the JavaScript
       execution stack.

    logs that a module at a given URL has been loaded.
    This function simply calls log, but can be overriden
    in other modules.  This function does not check
    whether `logging` is turned on; that's checked
    by `require`.
*/
modulesjs.logModule = function (url) {
    log(url, 'module');
};

/**** resolveObject
    resolves a URL Object relative the calling module or `modules.js`. 
    URLs beginning with ``.`` imply relation to the
    current module file.

    URL Objects are produced by `http.js#resolveObject` and have
    values for all of the `http.js#keys`.
*/
modulesjs.resolveObject = function (url) {
    var relativeTo;
    if (url.charAt(0) == ".") {
        if (this == modulesjs) {
            /* relative to the current page */
            relativeTo = environment;
        } else {
            /* relative to the current module */
            relativeTo = this;
        }
    } else {
        /* relative to modules.js */
        relativeTo = modulesjs;
    }

    return http.relativeObject(
        http.resolveObject(url, relativeTo.moduleScope.moduleUrl),
        modulesjsUrl
    );
};

/**** resolve
    resolves a URL relative the calling module or modules.js.  
    URLs beginning with ``.`` imply relation to the
    current module file.
*/
modulesjs.resolve = function (url) {
    return http.formatUrl(http.resolveObject.call(this, url));
};

/**** relativeObject
    returns an object containing named parts for a given URL
    as resolved as a module URL.
*/
modulesjs.relativeObject = function (url) {
    return http.relativeObject.call(this, url, modulesjsUrl);
};


/**** relative
    accepts a fully qualified URL and
    returns a URL string relative to a module implied by
    the context object.
*/
modulesjs.relative = function (url) {
    return http.relative.call(this, url, modulesjsUrl);
};

/**** dub
    decorates an object with attributes for their:

     * ``modules.js#moduleUrl``,
     * ``modules.js#name``, and
     * ``modules.js#fullName``

    denoting the module that it is from or bound to
    and the name it has in that scope.

    accepts:
     * an object,
     * a module URL, and
     * the name of the object.

*/
modulesjs.dub = function (member, moduleUrl, name) {
    if (member["modules.js#name"])
        return;
    member.name = name;
    member["modules.js#moduleUrl"] = moduleUrl;
    member["modules.js#name"] = name;
    var moduleUrl = http.parseUrl(moduleUrl);
    moduleUrl.anchor = name;
    member["modules.js#fullName"] = http.formatUrl(moduleUrl);
}

/**** require
    returns a `Module` for a given `moduleUrl`::
        
        var module = require(moduleUrl);

    blocks until the module has been loaded or the module
    object has been retrieved from the cache of singleton module
    objects.

    A `moduleUrl` is a `String` that describes the location
    of a JavaScript file relative to the `modulesUrl`, the path
    leading up to `modules.js` in the ``src`` attribute of its
    HTML script tag.  If the `moduleUrl` begins with a dot (``.``),
    the `moduleUrl` is resolved relative to the URL of
    the current `Module`.  Thus::

        var http = require('http.js');

    retrieves the `http.js` module that sits beside `modules.js`
    on the server.  However, in `browser/layout.js`::

        var html = require('./html.js');

    retrieves the `browser/html.js` module.
    
    supports `optional continuation passing style`::

        require(moduleUrl, function (module) {
        });

    If a `moduleUrl` and a `continuation` are provided,
    `require` returns `undefined` and instead passes
    the `Module` to the `continuation`.  If an exception
    is thrown during the execution of `require`, `require`
    will call the continuation with `undefined` as the
    first argument and the `Error` as a second argument.

    Currently, `require` blocks until the continuation finishes
    executing.  However, in future versions, the continuation
    passing style may be asynchronous.

*/
var requireDepth = 0;
modulesjs.require = function (url, continuation) {

    /* parse the URL into a mapping of key value pairs
     * relative to modules.js or the calling module */
    var urlObject = modulesjs.resolveObject.call(this, url);

    /*
        pare out the anchor and discard the query.
        we'll use the anchor to pare out a comment if
        it exists
    */
    var anchor = urlObject.anchor;
    urlObject.anchor = "";
    var url = http.formatUrl(urlObject);

    /* only load the module if it hasn't been loaded yet, or
     * a reload has been requested */
    if (!modulesjs.modules[url] || modulesjs.forceReload[url]) {
        delete modulesjs.forceReload[url];

        /*
            create a Module object if one doesn't already exist.
            otherwise, we'll reload the module on top
            of the old one.
        */
        if (!modulesjs.modules[url]) {
            modulesjs.modules[url] = Module(url);
        }

        var module = modulesjs.modules[url];
        var scope = module.moduleScope;
        var moduleUrl = scope.moduleUrl;

        /* handle registered modules */
        if (modulesjs.registeredModules[url]) {

            modulesjs.registeredModules[url].call(module);
            delete modulesjs.registeredModules[url];

        } else {

            /*
                load a module by fetching its text over
                http and evaluating it in a contrived
                scope chain and context.
            */

            var text;
            try {
                text = http.requestText(http.resolve(url, modulesjsUrl));
            } catch (exception) {
                throw new Error(
                    "RequireError: cannot get text of '" + url + "' " + 
                    "(" + exception + ")"
                );
            }

            requireDepth++;

            if (modulesjs.logging)
                modulesjs.logModule(url, requireDepth);

            try {
                /*
                    evaluate the module's text in a specially
                    prepared scope chain.
                */
                modulesjs.evalGlobalWith.call(module, text, module, scope);
            } catch (exception) {

                if (exception.lineNumber) {

                    /*
                        recursively note which module emits
                        an exception.
                    */
                    var offset = 0;
                    try {
                        modulesjs.evalGlobal("throw new Error()");
                    } catch (deliberate) {
                        offset = deliberate.lineNumber || 0;
                    };

                    exception.message += (
                        "\n on line number " +
                        (exception.lineNumber - offset + 1)
                    );
                    exception.lineNumber = 0;
                }

                exception.message += "\n in " + moduleUrl;
                throw exception;

            };

            requireDepth--;

        }

        /* annotate sources */
        for (var name in module) {
            var member = module[name];
            if (member instanceof Function) {
                modulesjs.dub(member, moduleUrl, name);
            }
        }

    }

    var module = modulesjs.modules[url];

    /*
        get the requested component from the module (specified after # in the URL)
        if one was provided
        as an URL anchor
    */
    if (anchor) {
        if (module[anchor] === undefined) {
            throw new Error(
                "There is no '" + anchor + "' component at '" +
                http.formatUrl(url) + "'."
            );
        }
        module = module[anchor];
    }

    /*
        handle both returning and continuation passing
    */
    if (continuation) {
        continuation(module);
    } else {
        return module;
    }
};

/**** include

    accepts a `moduleUrl`, returns a `Module`, and copies the items
    from the `Module` into the current `ModuleScope` except for
    the `moduleScope` item since this would mask the current module's
    scope::

        var base = require('base.js');
        include('base.js');
        assert(base.Set == moduleScope.Set == Set);

    `include` does not support continuation passing style.

    `include` supports an alternate argument.  If you provide
    `include` with an arbitrary object instead of a `Module`,
    it will copy the items of the object into the current
    `ModuleScope` instead.  This object can be a `Module`::

        include({
            'x': 10;
        });
        assert(x == 10);
    
    `include` also marks any imported
    functions with their foreign module's URL and name using `dub`.

    `include` doesn't work for `modules.js`.  Use `require`
    instead.  This is just a safety to prevent you from calling
    functions from `modules.js` that haven't been bound to
    the current module.  If you were able to `include`
    items from `modules.js` in your `ModuleScope`, using
    `include` thereafter would fail since the version
    of `include` in `modules.js` is not bound to the current
    `Module` and cannot copy items into its `ModuleScope`.

*/
modulesjs.include = function (source) {
    var targetScope = this.moduleScope;

    /* handle both URL's of modules and Objects */
    if (typeof source == "string") {
        source = modulesjs.require.call(this, source); 
    }

    /* Avoid including names like "include" and "require"
     * from modules.js in another module since they
     * would override the versions in moduleScope.  */
    if (source == modulesjs) {
        throw new Error("Do not include modules.js; use require.");
    }

    var sourceUrl = source.moduleScope ? source.moduleScope.moduleUrl : undefined;

    /* copy names from the object or module */
    for (var name in source) {
        if (name == 'moduleScope') continue;

        var member = source[name];

        if (member && member.foreignModuleBind) {
            if (member.foreignModuleBind) {
                member = targetScope.moduleBind(source, name);
                modulesjs.dub(
                    member,
                    source[name]['modules.js#moduleUrl'],
                    name
                );
                member.foreignModuleBind = true;
            }
        }

        targetScope[name] = member;

    }

    return source;
};

/**** publish
    copies items from an object onto the current
    `Module`.  This is handy for exporting items
    from another module, since `include` does not
    do this implicitly as ``import`` would in Python.

    accepts an `Object` or `Module` and returns
    `undefined`.

    This function takes special care not to replace the
    calling module's `moduleScope` object.
*/
modulesjs.publish = function (source) {
    var target = this;
    for (var name in source) {
        if (name == 'moduleScope') continue;
        target[name] = source[name];
    }
    return source;
};

/**** register

    replaces a `Module` in the singleton module cache with
    a given object.

    accepts:
     - a `moduleUrl`
     - a `Module`, a `Function`, or some other `Object`.
     - an optional boolean value indicating whether to force
       a reload of the module next time it's required or included.

    If you provide a `Module`, the `Module` replaces the
    current module in the cache.

    If you provide a `Function`, `register` treats the
    function as a constructor function for the a `Module`
    on the given URL.  The function will be called with a
    `Module` object as the context object (``this``)
    when it's required.  This particular feature is
    important for the module bundling step of the Chiron
    build process and permits multiple modules to be
    registered in a single file.

*/
modulesjs.register = function (url, module, _forceReload) {

    if (typeof module == "function") {
        modulesjs.registeredModules[url] = module;
        modulesjs.forceReload[url] = !!modulesjs.modules[url];
    } else if (module.moduleScope !== undefined) {
        modulesjs.modules[url] = module;
    } else {
        var module = Module(url, module);
        modulesjs.modules[url] = module;
    }

    if (_forceReload !== undefined)
        modulesjs.forceReload[url] = _forceReload;

};

/**** foreignModuleBind

    a decorator for functions that should receive
    the `Module` they're included in and called from
    as their context object (``this``)

    For example, if in module ``a.js`` we declare a ``logModule``
    exportable function with the ``foreignModuleBind`` decorator::

        this.logModule = foreignModule(function () {
            log(this.moduleScope.moduleUrl);
        });
    
    And we include and call ``logModule`` in ``b.js``::

        include('a.js');
        logModule();

    ``logModule`` will receive the `Module` for ``b.js`` as
    ``this`` instead of the global context object (``window``) or the
    ``a.js`` `Module`.  Thus, ``b.js`` would print to the console::

        b.js

*/
modulesjs.foreignModuleBind = function (bindee) {
    bindee.foreignModuleBind = true;
    return bindee;
};

/**** modules
    a mapping of URL's to singleton `Module` objects for
    each module that is being or has been loaded.
*/
modulesjs.modules = {};

/**** registeredModules
    an `Object` used as a dictionary of URL's to module
    constructor functions.  `register` adds elements
    to this mapping, and `require` uses this mapping to
    construct modules from functions in preloaded module
    bundels.
*/
modulesjs.registeredModules = {};

/**** forceReload
    an `Object` used as a set of URL's of modules that
    should be reloaded and evaluated next time they're
    required (or, by implication, included).
*/
modulesjs.forceReload = {};

/**** evalGlobalWith
    evaluates given text in global scope, such that evaluated scripts
    do not have an opportunity to accidentally access or manipulate
    anything but global variables, using `evalGlobal`, internally.

    `evalGlobalWith` accepts some optional
    scope objects to evaluate the text within, so the user has the option of
    inserting variables for the script's use.

    Usage::

        evalGlobalWith(text)
            function () { eval(text) }.apply(this, arguments)

        evalGlobalWith(text, scope)
            with (scope) {
            function () { eval(text) }.apply(this, arguments) }}

        evalGlobalWith(text, innerScope, outerScope)
            with (outerScope) {
            with (innerScope) {
            function () { eval(text) }.apply(this, arguments) }}}

        evalGlobalWith(text, innerScope, ..., outerScope)

*/
modulesjs.evalGlobalWith = function (/*text, scope, scope, scope, ... */) {

    /* this is deep magic */

    /*
        construct a text that can be evaluated within a naked global eval that
        will accept this text and in turn eval the next argument,
        our given inner text (arguments[0] here, arguments[1] inside)
    */
    var text = "(function () {" +
        "return eval(arguments[1])" + 
    "}).apply(this, arguments)";

    /*
        wrap the eval in a with block for every scope from inside to out
        provided as additional, variadic arguments
    */
    for (var i = 1; i < arguments.length; i++) {
        text = "with (arguments[" + (i + 1) + "]) { " + text + " }";

    }

    /*
        pass all of the same arguments to `evalGlobal` but add
        the "text" to the beginning
    */
    var args = [text];
    /*
        `push.apply` is faster than `concat` since it doesn't need
        to construct as many new `Array` objects
    */
    args.push.apply(args, Array.prototype.slice.call(arguments, 0));

    return modulesjs.evalGlobal.apply(this, args);
};

/**** evalGlobal
    a version of `eval` that guarantees that only global names
    are available inside the given script.
    `evalGlobal` blocks until the evaluation is complete
    and returns the last value evaluated.
    `evalGlobal` exists in response to the need to protect
    variables in the calling scope.  `eval` permits the
    given program to access the caller's scope chain::

        (function () {

            var a = 10;
            eval("a = 20");
            a == 20;

            var b = 10;
            evalGlobal("b = 20");
            b == 10;
            window.b == 20;

        })();

    The `arguments` object is a good way to pass and
    access variables inside of an `evalGlobal` program::

        evalGlobal("arguments[1]", 10) == 10;

*/
modulesjs.evalGlobal = evalGlobal;
/*
    `evalGlobal` is defined at the end of this script, outside the enclosure,
    so that it can't access module modulesjs internal variables.
*/


/**
    Environment
    ===========
*/

/*** environment.js
*/
var environment = modulesjs.environment = Module("environment.js");

/**** messages
*/
environment.messages = [];

/**** log

    accepts:
     - a ``message`` and 
     - an optional ``label``.

    The label, by convention, is one of `"log"``, ``"info"``,
    ``"warn"``, or ``"error"``.  Custom loggers treat labels like
    ``"module"``, ``"pass"``, or ``"fail"``.  Attempts to write
    the message to `window.console`, progressively handling
    `console` implementations that provide a function for the 
    given ``label``, or defaulting to `log` depending on
    availability.

    Also adds a ``[message, label, moduleUrl]`` array to the end
    of `environment.messages`.  ``label`` is one of ``"log"``,
    ``"warn"``, ``"info"``, or ``"error"`` by convention.  `moduleUrl`
    is the URL of the module from which `log` was called.

    In Safari, `log` writes to the Javascript debug console, which
    is only available if you set the preference::

        defaults write com.apple.Safari IncludeDebugMenu 1

    Or in Safari 3::

        defaults write com.apple.Safari IncludeDevelopMenu 1

    In Firefox, you can get a debug console with Firebug,
    http://getfirebug.com.

    You can override the behavior of `log` by assigning
    a different function to ``require('environment.js').log``
    in any module.

    Chiron can create a debug console for the purpose of
    unit testing or page debugging.  To debug a web page,
    use `modules.js` to include `debug.js` on a page.
    To run a unit test, view `run.html`, `lite.html`,
    or `edit.html` with the `moduleUrl` of the unit test
    as a query string.

    see:
     - `error`
     - `warn`
     - `info`
*/
var log = environment.log = function (message, label, source) {

    var module = this.moduleScope ? this : modulesjs;
    label = arguments.length == 1  ? "log" : label;
    if (!source) source = module.moduleScope.moduleUrl;

    /*
        buffer messages so that console overrides
        can retrieve and display them later.
    */
    environment.messages.push([message, label, source]);

    /*
        attempt to send the message to window.console if it
        exists, progressively handling the availability
        of a logging function crafted especially for the
        requested label, or defaulting to 'log'.
    */

    if (environment.console) {
        if (environment.console.general) {
            environment.console.general(message, label, source);
        } else if (environment.console[label]) {
            environment.console[label](message);
        } else if (environment.console.log) {
            environment.console.log(message);
        }
    }

};

environment.console = window.console;

/**** warn
    writes a warning message to `window.console.warn` or
    defers to `log`.

    see:
     - `log`
     - `error`
     - `info`
*/
var warn = environment.warn = function (message) {
    log.call(this, message, "warn");
};

/**** error
    writes an error message to `window.console.error` or
    defers to `log`.  Accepts a `String` or an `Error`.

    see:
     - `log`
     - `warn`
     - `info`
*/
var error = environment.error = function (message) {
    if (message instanceof Error)
        message = message.message;
    log.call(this, '' + message, "error");
};

window.onerror = error;

/**** info
    writes an informational message to `window.console.info`
    or defers to `log` depending on the console.

    see:
     - `log`
     - `error`
     - `warn`
*/
var info = environment.info = function (message) {
    log.call(this, message, "info");
};

with (environment) {

    environment.appId = navigator.userAgent;
    environment.appVersion = navigator.appVersion;
    environment.appVersionParts = environment.appVersion.split(' ', 1)[0].split('.');
    environment.appVersionMajor = Number(environment.appVersionParts[0]);
    environment.appVersionMinor = Number(environment.appVersionParts[1]);

    /**
        Application
        -----------
    */
    /**** app
        one of ``"browser"``, ``"dashboard"``, ``"spiderMonkey"``, 
        or ``"rhino"``.
    */
    environment.app = function () { /* enclosure */
        if (this.load) {
            if (this.Packages) return "rhino";
            return "spiderMonkey";
        }
        if (this.widget) return "dashboard";
        return "browser";
    }();
    /**** isBrowser */
    environment.isBrowser = app == "browser";
    /**** isDashboard */
    environment.isDashboard = app == "dashboard";
    /**** isRhino */
    environment.isRhino = app == "rhino";
    /**** isSpiderMonkey */
    environment.isSpiderMonkey = app == "spiderMonkey";

    /**
        Browser
        -------
    */
    /**** browser 
        one of ``"gecko"``, ``"safari"``, ``"konqueror"``,
        or ``"explorer"``, ``undefined``.
    */
    environment.browser = function () { /* enclosure */
        if (appVersion.indexOf("Safari") >= 0) return "safari";
        if (appVersion.indexOf("Konqueror") >= 0) return "konqueror";
        if (appId.indexOf("Gecko") >= 0) return "gecko";
        if (this.opera != undefined) return "opera";
        if (document.all != undefined) return "explorer"; /* and not opera */
    }();
    /**** isOpera */
    environment.isOpera = isBrowser && window.opera;
    /**** isKonqueror */
    environment.isKonqueror = isBrowser && browser == "konqueror";
    /**** isWebKit */
    environment.isWebKit = isBrowser && appId.indexOf("AppleWebKit/") >= 0;
    /**** isSafari */
    environment.isSafari = isBrowser && browser == "safari";
    /**** isKhtml */
    environment.isKhtml = isBrowser && (
        appVersion.indexOf("KHTML") >= 0 ||
        isKonqueror ||
        isSafari
    );
    /**** isGecko */
    environment.isGecko = (
        isBrowser &&
        !isKhtml &&
        appId.indexOf("Gecko") >= 0
    );
    environment.isMozilla = isGecko;
    /**** isFirefox */
    environment.isFirefox = isBrowser && appId.indexOf("Firefox") >= 0;
    /**** isIE */
    environment.isIE = isBrowser && appVersion.indexOf("MSIE") >= 0;
    /**** isIE5 */
    environment.isIE5 = isIE && appVersion.indexOf("MSIE 5.0") >= 0;
    /**** isIE5_5 */
    environment.isIE5_5 = isIE && appVersion.indexOf("MSIE 5.5") >= 0;
    /**** isIE6 */
    environment.isIE6 = isIE && appVersion.indexOf("MSIE 6.0") >= 0;
    /**** isIE7 */
    environment.isIE7 = isIE && appVersion.indexOf("MSIE 7.0") >= 0;

    /**
        System
        ------
    */

    /*** isWindows
    */
    environment.isWindows = appVersion.indexOf('Windows') >= 0;

    /*** isMac
    */

    environment.isMac = appVersion.indexOf('Macintosh') >= 0;

    /*** isUnix
    */
    environment.isUnix = appId.indexOf('X11') >= 0;

    /**** url */
    environment.url = isBrowser ? window.location.href : ".";

    /**** locale */
    environment.locale = (
        isIE ? navigator.userLanguage : navigator.language
    ).toLowerCase();

    /**** quirksMode */
    environment.quirksMode = (
        document.compatMode == 'BackCompat' ||
        document.compatMode == 'QuircksMode' ||
        isIE && document.documentElement.clientHeight == 0
    );

}

/**
    Browser Base
    ============
*/

/*** browser.js
*/
var browser = Module("browser.js");

/* A table of name translations that hasAttr uses.
 * However, to save space and time, the translations are
 * only available if you require `browser.js`.
 * The module is reloaded on demand using the actual `browser.js`
 * file.
 * In the context of `modules.js`, none of these translations
 * are necessary, so the mapping is empty.
 */
browser.translations = {};

/**** hasAttr */
browser.hasAttr = function () { /* closure */
    if (environment.isIE) {
        return function (element, key) {
            if (browser.translations[key]) {
                warn(
                    "Please use '" + browser.translations[key] +
                    "' instead of '" + key + "'."
                );
                key = browser.translations[key];
            }
            var node = element.getAttributeNode(key);
            return node && node.specified;
        };
    } else {
        return function (element, key) {
            return element.hasAttribute(key);
        };
    }
}();

/**** getAttr */
browser.getAttr = function (element, key) {
    return element.getAttribute(key);
};

/**
    HTTP
    ====

    ''a.k.a., AJAX''

    Uses regular expressions designed by Steven Levithan [#parseUri]_.
*/

/*** http.js
*/
var http = modulesjs.http = Module("http.js");

/**** keys
    members of a parsed URI object.
*/
http.keys = [
    "url",
    "protocol",
    "authorityRoot",
    "authority",
        "userInfo",
            "user",
            "password",
        "domain",
            "domains",
        "port",
    "path",
        "root",
        "directory",
            "directories",
        "file",
    "query",
    "anchor"
];

/**** expressionKeys
    members of a parsed URI object that you get
    from evaluting the strict regular expression.
*/
http.expressionKeys = [
    "url",
    "protocol",
    "authorityRoot",
    "authority",
        "userInfo",
            "user",
            "password",
        "domain",
        "port",
    "path",
        "root",
        "directory",
        "file",
    "query",
    "anchor"
];

/**** strictExpression
*/
http.strictExpression = new RegExp( /* url */
    "^" +
    "(?:" +
        "([^:/?#]+):" + /* protocol */
    ")?" +
    "(?:" +
        "(//)" + /* authorityRoot */
        "(" + /* authority */
            "(?:" +
                "(" + /* userInfo */
                    "([^:@]*)" + /* user */
                    ":?" +
                    "([^:@]*)" + /* password */
                ")?" +
                "@" +
            ")?" +
            "([^:/?#]*)" + /* domain */
            "(?::(\\d*))?" + /* port */
        ")" +
    ")?" +
    "(" + /* path */
        "(/?)" + /* root */
        "((?:[^?#/]*/)*)" +
        "([^?#]*)" + /* file */
    ")" +
    "(?:\\?([^#]*))?" + /* query */
    "(?:#(.*))?" /*anchor */
);

/**** urlParser
    returns a URI parser function given
    a regular expression that renders 
    `expressionKeys` and returns an `Object`
    mapping all `keys` to values.
*/
http.urlParser = function (expression) {
    return function (url) {
        if (typeof url == "undefined")
            throw new Error("HttpError: URL is undefined");
        if (typeof url != "string") return new Object(url);

        var items = {};
        var parts = expression.exec(url);

        for (var i = 0; i < parts.length; i++) {
            items[http.expressionKeys[i]] = parts[i] ? parts[i] : "";
        }

        items.root = (items.root || items.authorityRoot) ? '/' : '';

        items.directories = items.directory.split("/");
        if (items.directories[items.directories.length - 1] == "") {
            items.directories.pop();
        }

        /* normalize */
        var directories = [];
        for (var i = 0; i < items.directories.length; i++) {
            var directory = items.directories[i];
            if (directory == '.') {
            } else if (directory == '..') {
                if (directories.length && directories[directories.length - 1] != '..')
                    directories.pop();
                else
                    directories.push('..');
            } else {
                directories.push(directory);
            }
        }
        items.directories = directories;

        items.domains = items.domain.split(".");

        return items;
    };
};

/**** parseUrl
    a strict URI parser.
*/
http.parseUrl = http.urlParser(http.strictExpression);

/**** formatUrl
    accepts a parsed URI object and returns
    the corresponding string.
*/
http.formatUrl = function (object) {
    if (typeof(object) == 'undefined')
        throw new Error("HttpError: URL not defined for format");
    if (object instanceof String || typeof(object) == 'string')
        return object;
    var domain =
        object.domains ?
        object.domains.join(".") :
        object.domain;
    var userInfo = (
            object.user ||
            object.password 
        ) ?
        (
            (object.user || "") + 
            (object.password ? ":" + object.password : "") 
        ) :
        object.userInfo;
    var authority = (
            userInfo ||
            domain ||
            object.port
        ) ? (
            (userInfo ? userInfo + "@" : "") +
            (domain || "") + 
            (object.port ? ":" + object.port : "")
        ) :
        object.authority;
    var directory =
        object.directories ?
        object.directories.join("/") :
        object.directory;
    var path =
        directory || object.file ?
        (
            (directory ? directory + "/" : "") +
            (object.file || "")
        ) :
        object.path;
    return (
        (object.protocol ? object.protocol + ":" : "") +
        (authority ? "//" + authority : "") +
        (object.root || (authority && path) ? "/" : "") +
        (path ? path : "") +
        (object.query ? "?" + object.query : "") +
        (object.anchor ? "#" + object.anchor : "")
    ) || object.url || "";
};

/**** resolveObject
    returns an object representing a URL resolved from
    a relative location and a base location.
*/
http.resolveObject = function (relative, base) {
    if (!base) base = window.location.href;

    base = http.parseUrl(base);
    relative = http.parseUrl(relative);

    if (relative.url == "")
        return base;

    delete base.url;
    delete base.authority;
    delete base.domain;
    delete base.userInfo;
    delete base.path;
    delete base.directory;

    if (
        relative.protocol && relative.protocol != base.protocol ||
        relative.authority && relative.authority != base.authority
    ) {
        base = relative;
    } else {
        if (relative.root) {
            base.directories = relative.directories;
        } else {

            var directories = relative.directories;
            for (var i = 0; i < directories.length; i++) {
                var directory = directories[i];
                if (directory == ".") {
                } else if (directory == "..") {
                    if (base.directories.length) {
                        base.directories.pop();
                    } else {
                        base.directories.push('..');
                    }
                } else {
                    base.directories.push(directory);
                }
            }

            if (relative.file == ".") {
                relative.file = "";
            } else if (relative.file == "..") {
                base.directories.pop();
                relative.file = "";
            }
        }
    }

    if (relative.root)
        base.root = relative.root;
    if (relative.protcol)
        base.protocol = relative.protocol;
    if (!(!relative.path && relative.anchor))
        base.file = relative.file;
    base.query = relative.query;
    base.anchor = relative.anchor;

    return base;
};

/**** relativeObject
    returns an object representing a relative URL to
    a given target URL from a source URL.
*/
http.relativeObject = function (target, base) {
    target = http.parseUrl(target);
    base = http.parseUrl(base);

    delete target.url;

    if (
        target.protocol == base.protocol &&
        target.authority == base.authority
    ) {
        delete target.protocol;
        delete target.authority;
        delete target.userInfo;
        delete target.user;
        delete target.password;
        delete target.domain;
        delete target.domains;
        delete target.port;
        if (
            !!target.root == !!base.root && !(
                target.root &&
                target.directories[0] != base.directories[0]
            )
        ) {
            delete target.path;
            delete target.root;
            delete target.directory;
            while (
                base.directories.length &&
                target.directories.length &&
                target.directories[0] == base.directories[0]
            ) {
                target.directories.shift();
                base.directories.shift();
            }
            while (base.directories.length) {
                base.directories.shift();
                target.directories.unshift('..');
            }

            if (!target.root && !target.directories.length && !target.file && base.file)
                target.directories.push('.');

            if (base.file == target.file)
                delete target.file;
            if (base.query == target.query)
                delete target.query;
            if (base.anchor == target.anchor)
                delete target.anchor;
        }
    }

    return target;
};

/**** resolve
    returns a URL resovled to a relative URL from a base URL.
*/
http.resolve = function (relative, base) {
    return http.formatUrl(http.resolveObject(relative, base));
};

/**** relative
    returns a relative URL to a target from a source.
*/
http.relative = function (target, base) {
    return http.formatUrl(http.relativeObject(target, base));
};

/**** requestText
    returns the text at a given URL using an HTTP
    request.
    supports continuation passing form for asynchronous
    requests.
*/
http.requestText = function (url, observer) {
    if (observer !== undefined) {
        return http.request(url, function (response) {
            if (response.isError())
                observer(undefined, new Error(response.getStatus()));
            else
                observer(response.getContent());
        });
    } else {
        var response = http.request(url);
        if (response.isError())
            throw new Error(response.getStatus());
        return response.getContent();
    }
};

/**** requestXml
    returns an XML DOM from a page on a given URL
    using an HTTP request.
    supports continuation passing form for asynchronous
    requests.
*/
http.requestXml = function (url, observer) {
    if (observer !== undefined) {
        return http.request(url, function (response) {
            observer(response.getXml());
        });
    } else {
        var response = http.request(url);
        if (response.isError())
            throw new Error(response.getStatus());
        return response.getXml();
    }
};

/**** requestDocument
    returns an XML document element from a page
    on a given URL using an HTML request.
    supports continuation passing form for asynchronous
    requests.
*/
http.requestDocument = function (url, observer) {
    if (observer !== undefined) {
        return http.request(url, function (response) {
            observer(response.getDocument());
        });
    } else {
        var response = http.request(url);
        if (response.isError())
            throw new Error(response.getStatus());
        return response.getDocument();
    }
};

/**** synchronous
    a constant for specifying scnchronous, blocking,
    waiting HTTP requests.
*/
http.synchronous = false;

/**** asynchronous
    a constant for specifying asynchronous, non-blocking
    HTTP requests.
*/
http.asynchronous = true;

/**** request
    sends an HTTP request to a given URL and returns
    the response.
    supports continuation passing form for asynchronous
    requests.
*/
http.request = function (url, observer) {
    var request = http.Request();
    var response = request.getResponse();

    url = http.resolve(url, window.location.href);

    if (observer)
        request.observe("ok", observer);

    request.open("GET", url, !!observer);

    try {
        request.send();
    } catch (exception) {
        request.abort();
        throw new Error('HttpError: "' + url + '": ' + exception);
    }

    if (observer !== undefined) {
        return request;
    } else {
        return response;
    }

};

/*todo
    Look deeper into dojo/src/hostenv_browser.js for 
    _blockAsync lock to prevent eternal hanging in KHTML
*/

/**** Request
    returns a wrapped HTTP Request object.
*/
http.Request = function () {
    /* this line permits the user to create a request with
     * either new Request() or Request().  internally,
     * we just use the Request() so that Request can be
     * overloaded later in http.js to be a type constructor
     * function instead of a prototype constructor */
    if (this == http) return new http.Request();

    var self = this;
    var method, url, asynchronous, user, password;
    var realRequest = http.createNativeRequest();
    var response = http.Response(realRequest);
    var isOpen;
    var readyStateChanged;
    var timeout;
    var timeoutHandle;
    var aborted;

    var readyObservers = [];
    var okObservers = [];
    var errorObservers = [];
    var warnObservers = [];
    var timeoutObservers = [];

    /***** toString
    */
    self.toString = function () {return "[object HttpRequest]"};

    /***** getResponse
    */
    self.getResponse = function () {
        return response;
    };

    var signal = function (response, observers) {
        while (observers.length) {
            var observer = observers.shift();
            observer(response);
        }
    };

    /***** pogress
        an event function that the Request calls when it
        receives a chunk of content.
    */
    self.progress = function () {
        /* necessary: this function becomes an observable signal */
    };

    /***** ready
        an event function that the Request calls when
        the Reponse is ready.
    */
    self.ready = function () {
        signal(response, readyObservers);
    };

    /***** ok
        an event function that the Request calls when a Response
        is ready and all went well.  Note that Safari and FireFox, at least,
        will fire this event even when the connection is abruptly
        terminated by the server, reporting a 200 status and
        an empty response content.
    */
    self.ok = function () {
        signal(response, okObservers);
    };

    /***** error
        an event function that the Request calls when a Reponse
        is completed but failed to retrieve the requested content.
    */
    self.error = function () {
        signal(response, errorObservers);
    };

    /***** warn
        an event function that the Request calls when 
        something is amiss with message.
    */
    self.warn = function (message) {
        warn(message);
        signal(response, warnObservers);
    };

    /***** timeout
        an event function that Request calls when a request
        times out.  The default behavior is to invoke an error.
    */
    self.timeout = function () {
        signal(response, timeoutObservers);
    };

    /***** observe
        permits a user to observe `ready`, `ok`,
        `error`, and `warn` events with a handler
        function.  Observing any event on a `Request`
        causes the `open` and `send` to implicitly become
        asynchronous.
    */
    self.observe = function (eventName, observer) {
        asynchronous = true;
        if (eventName == "ready") readyObservers.push(observer);
        else if (eventName == "ok") okObservers.push(observer);
        else if (eventName == "error") errorObservers.push(observer);
        else if (eventName == "warn") warnObservers.push(observer);
        else if (eventName == "timeout") timeoutObservers.push(observer);
        else throw new Error(
            "HttpError: event name '" + eventName + "' " +
            "is not recognized"
        );
    };

    /***** setHeader
    */
    self.setHeader = function (key, value) {
        realRequest.setRequestHeader(key, value);
    };

    /***** isOpen
    */
    self.isOpen = function () {
        return isOpen;
    };

    /***** isSent
    */
    self.isSent = function () {
        return realRequest.readyState > 0;
    };

    /***** getTimeout
    */
    self.getTimeout = function () {
        return timeout;
    };

    /***** setTimeout
    */
    self.setTimeout = function (value) {
        timeout = value;
    };

    /***** open

        Accepts

         - ``method``, an HTTP request method, for example,
           ``GET``, ``POST``, ``PROPFIND`` and others.
         - ``url``, a web location string
         - ``synchronous``, whether ``send`` will block until completed,
           for example, ``http.synchronous``, ``http.asynchronous``.
         - ``user``, an optional HTTP user name.
         - ``password``, an optional HTTP password.

    */
    self.open = function (_method, _url, _asynchronous, _user, _password) {
        try {
            return realRequest.open(
                method = _method,
                url = _url,
                asynchronous = _asynchronous,
                user = _user,
                password = _password
            );
        } finally {
            isOpen = true;
        }
    };

    /***** send
        Accepts an optional ``content`` argument for requests like ``POST`` method.
    */
    self.send = function (content) {

        realRequest.setRequestHeader("X-Requested-With", "XMLHttpRequest");

        if (!content) {
            content = "";
        }

        if (timeout !== undefined) {
            timeoutHandle = setTimeout(function () {
                timeoutHandle = undefined;
                var status = response.getStatus();
                if (status != 200 || status === undefined) {
                    self.timeout();
                    self.abort();
                }
            }, timeout);
        }

        return realRequest.send(content);
    };

    /***** abort
    */
    self.abort = function () {
        if (timeoutHandle !== undefined)
            clearTimeout(timeoutHandle);
        aborted = true;
        return realRequest.abort();
    };

    realRequest.onreadystatechange = function () {
        readyStateChanged = true;

        self.progress();

        if (aborted) {
            free();
        } else if (realRequest.readyState == 4) {
            try {

                self.ready(response);

                if (response.isOk()) {
                    self.ok(response);
                } else {
                    self.error(response);
                }

            } catch (exception) {
                error(exception.message || exception);
            }
            free();
        }

    };

    var free = function () {
        /* break the reference cycle */
        delete realRequest['onreadystatechange'];
        realRequest.onreadystatechange = undefined;
        realRequest = undefined;
    };

    return self;
};

/**** Response
    returns a wrapped HTTP Response object.
*/
http.Response = function (realRequest) {
    /* this line permits the user to create a request with
     * either new Respones() or Response().  internally,
     * we just use the Response() so that Response can be
     * overloaded later in http.js to be a type constructor
     * function instead of a prototype constructor */
    if (this == http) return new http.Response(realRequest);

    var self = this;

    /* this init function doesn't get invoked until Response becomes
     * a type in http.js.  so, this method is merely for the future. */
    self.init = function (realRequestValue) {
        realRequest = realRequestValue;
    };

    /***** isReady
        whether the request is finished.  This indicates
        whether you can call `getStatus`
    */
    self.isReady = function () {
        return realRequest.readyState == 4;
    };

    /***** getStatus
        returns the HTTP response code.  Local files
        return 0.  Returns ``undefined`` if the
        underlying XML HTTP request throws an exception,
        `getStatus` returns ``undefined``.
    */
    self.getStatus = function () {
        /* one wouldn't think this were necessary.
         * one would be wrong. */
        try {
            return realRequest.status;
        } catch (exception) {
            return undefined;
        }
    };

    /***** isOk
        returns whether a request had a valid response.
        This usually is indicative of a 200 HTTP response
        code, but there are variations among browsers.

        HTTP Status codes in the interval [200, 300] are all legal
        HTTP Ok responses. 

        In Firefox and Safari 3, local files acquired with an HTTP request
        have a status code of 0.

        In Safari 2, local files acquired with an asynchronous HTTP
        request have a status of undefined.

        In Safari, a response with no content causes a status
        of `undefined`.

        Konqueror requires acceptance of 304, "using cache",
        according to dojo/src/hostenv_browser.js

        According to jQuery issue #1450, IE sometimes 1223
        instead of 204.
    */
    self.isOk = function () {
        var status = self.getStatus();
        return (
            /* usually */
            status >= 200 && status < 300 ||
            /* Firefox and Safari 3 file:// */
            status == 0 ||
            /* Konqueror using cache */
            status == 304 ||
            /* IE bug 1223 */
            status == 1223 ||
            /* Safari 2 asynchronous file:// and
              all Safari for no file content */
            (environment.isSafari && status == undefined && (
                /^file:\/\//.test(url) ||
                realRequest.responseText == ""
            ))
        );
    };

    /***** isError
    */
    self.isError = function () {
        return !self.isOk();
    };

    /***** getContent
    */
    self.getContent = function () {
        return realRequest.responseText;
    };

    /***** getText
         - `deprecated`, use `getContent`
    */
    self.getText = function () {
        return realRequest.responseText;
    };

    /***** getXml
    */
    self.getXml = function () {
        /*todo account for Explorer's lack of responseXML using
        the ActiveX provided by Dean Edwards */
        if (realRequest.responseXML == undefined) {
            throw new http.Error("HttpError: HTTP Response is not XML");
        }
        return realRequest.responseXML;
    };

    /***** getDocument
    */
    self.getDocument = function () {
        return self.getXml().documentElement;
    };

    /***** getHeader
    */
    self.getHeader = function (key) {
        return realRequest.getResponseHeader(key);
    };

    /***** hasHeader
    */
    self.hasHeader = function (key) {
        return realRequest.getResponseHeader(key) != undefined;
    };

    /***** getHeaders
    */
    self.getHeaders = function () {
        var headers = realRequest.getAllResponseHeaders();
        if (!headers) return {};
        return headers;
    };

    /***** len
    */
    self.len = function () {
        return realRequest.responseText.length;
    };

};

/**** createNativeRequest
    returns an XMLHttpRequest in most browsers.
*/
/* Based on dojo/src/hostenv_browser.js */

http.createNativeRequest = function () {
    /*

        subscribes to the lazy function definition pattern, since it
        redefines itself as the first method that works on the first
        call.

        Some other AJAX implementations check
         - Msxml2.XMLHTTP.6.0
         - Msxml2.XMLHTTP.5.0
         - Msxml2.XMLHTTP.4.0
         - Msxml2.XMLHTTP.3.0
         - Microsoft.XMLHTTP

        Microsoft.XMLHTTP is an older name-space, but is equivalent to
        the more lucid Msxml2.XMLHTTP.3.0 and only available when the
        latter is available too.

        Msxml2.XMLHTTP.4.0 has been superseded and is currently only
        intended to support legacy applications.

        Msxml2.XMLHTTP.5.0 was shipped with MS Office 2003 and was
        intended for Office applications. IE7 has this component off
        by default in the Internet zone, leading to canary-yellow
        verification dialogs.

        Msxml2.XMLHTTP.6.0 is currently the standard MS is pushing.
        I originally left out 6.0 since it would increase the burden
        of testing for functionality that cannot be trusted to work
        in all browsers.
        However, I've taken Jonathan Snook's advice to check for
        Microsoft's latest and greatest.

        see: http://snook.ca/archives/javascript/xmlhttprequest_activex_ie/

        Msxml2.XMLHTTP.3.0 is the most widely deployed version and is
        serviced regularly with the OS for security and other reasons.
        It is MS's preferred alternative to MSXML6.

        see: http://blogs.msdn.com/xmlteam/archive/2006/10/23/using-the-right-version-of-msxml-in-internet-explorer.aspx

        see: http://www.telerik.com/documents/AJAX%20Page/Ajax-Part1.pdf page 3

    */

    var trials = [
        function () {return new XMLHttpRequest()},
        function () {return new ActiveXObject("Msxml2.XMLHTTP.6.0")},
        function () {return new ActiveXObject("Msxml2.XMLHTTP.3.0")},
        function () {throw new Error("No HTTP Request object available for your environment.")}
    ];

    var trial, result, exception;
    for (var i = 0; i < trials.length; i++) {
        exception = undefined;
        /* redeclare for posterity */
        http.createNativeRequest = trial = trials[i];
        try {
            result = trial();
        } catch (trialException) {
            exception = trialException;
            continue;
        }
        break;
    }

    if (exception) throw exception;
    else return result;
};

/* grab the URL of modules.js relative to the containing page.
/* depends on browser and http */
var modulesjsUrl = modulesjs.modulesUrl = http.resolve(function () { /* enclosure */
    var scripts = document.getElementsByTagName("script");
    for (var i = 0; i < scripts.length; i++) {
        var script = scripts[i];
        if (browser.hasAttr(script, "src")) {
            var src = browser.getAttr(script, "src");
            src = http.parseUrl(src);
            if (src.file == "modules.js") {
                script.parentNode.removeChild(script);
                return http.resolve(src, window.location.href);
            }
        }
    }
}());


/* add the window url to the environment */
/* depends on http and environment */
(function () { /* enclosure */
    environment.moduleScope.moduleUrl = modulesjs.relative(window.location.href);
    var items = http.parseUrl(environment.moduleScope.moduleUrl);
    for (var i = 0; i < http.keys.length; i++) {
        var key = http.keys[i];
        var value = items[key];
        environment[key] = value;
    };
})();


/*
    depends on http:
    these calls must come after the http module since they
    require URL resolution.
*/
modulesjs.register("modules.js", modulesjs);
modulesjs.register("environment.js", environment);
modulesjs.register("browser.js", browser, true);
modulesjs.register("http.js", http, true);


/*
    warn the developer if they are not in standards complaince.
*/
(function () {
    var mode = document.compatmode;
    if (mode == "BackCompat")
        warn("Warning: quirks mode is on.  " + 
            "Not all rendering will be standards compliant.");
})();

/*
    this value is documented near beginTime and readyTime in the modules.js
    library section.
*/
modulesjs.endTime = new Date();

/**
    As a final pass, `modules.js` accepts an optional, ampersand-delimited list
    of module URL's specified in the `modules.js` query string.  When the
    module loader is ready and the DOM and CSS are fully loaded, it automatically
    uses `require` to load these modules.  If you do not take advantage
    of this list, there is now way to use the module system
    since it leaves no toe-hold in global scope.  The best practice is to at least
    require a module specific to your page, or use `chiron.js` to 
    enable the use of namespaced inline scripts.
*/

var isReady = false;
var ready = function () {
    if (isReady) return;
    isReady = true;
    /* this value is document near beginTime and endTime at the beginning
     * of the modules.js documentation */
    modulesjs.readyTime = new Date();

    /* note for CSS that JavaScript is enabled */
    document.body.className = document.body.className + ' javascript';

    var url = http.parseUrl(modulesjsUrl);
    if (url.query != "") {
        var urls = url.query.split("&");
        for (var i = 0; i < urls.length; i++) {
            var module = Module(modulesjs.relative(window.location.href, modulesjsUrl));
            var moduleScope = module.moduleScope;
            (function (url) {
                /* use a timeout so that each module gets a 
                 * full stack to work in */
                setTimeout(function () {
                    try {
                        moduleScope.require(url);
                    } catch (exception) {
                        if (exception.message) {
                            error(exception.message);
                        } else {
                            error('' + exception);
                        }
                    }
                }, 0);
            })(urls[i]);
        }
    }
    environment.info('ready');
};

/*
    wait for the DOM and CSS to be ready, but don't wait
    for images unless they're absolutely necessary.
*/

/*
    ported from jQuery's event.js, with previous implementations
    taking from similar sources, including Dean Edwards
    and PPK.
*/

/*
    Opera uses DOMContentLoaded but has special code for
    pending style sheets.
*/
if (environment.isOpera)
    document.addEventListener("DOMContentLoaded", function () {
        if (isReady) return;
        for (var i = 0; i < document.styleSheets.length; i++)
            if (document.styleSheets[i].disabled) {
                setTimeout(arguments.callee, 0);
                return;
            }
        // and execute any waiting functions
        ready();
    }, false);

/* Mozilla and WebKit nightlies currently support this event */
if (document.addEventListener)
    /* Use the handy event callback */
    document.addEventListener("DOMContentLoaded", ready, false);

/*
    If IE is used and is not in a frame,
    continually check to see whether the document is ready.
*/
if (environment.isIE && window == top) (function () {
    if (isReady) return;
    try {
        /*
            If IE is used, use the trick by Diego Perini
            http://javascript.nwbox.com/IEContentLoaded/
        */
        document.documentElement.doScroll("left");
    } catch (error) {
        /*
            using setTimeout with a 0 milisecond dellay
            is effectively the equivalent of a "yield"
            in a cooperative multi-task language.
            This permits the browser to breathe before
            we check whether we're ready again.
        */
        setTimeout(arguments.callee, 0);
        return;
    }
    ready();
})();

if (environment.isSafari) {
    (function () {
        if (isReady) return;
        if (
            document.readyState != "loaded" &&
            document.readyState != "complete"
        ) {
            setTimeout(arguments.callee, 0);
            return;
        }
        var numStyles = document.getElementsByTagName('style').length;
        var links = document.getElementsByTagName('link');
        for (var i = 0; i < links.length; i++) {
            var link = links[i];
            numStyles += (
                link.hasAttribute('rel') &&
                link.getAttribute('rel').toLowerCase() ==
                'stylesheet'
            );
        }
        if (document.styleSheets.length != numStyles) {
            setTimeout(arguments.callee, 0);
            return;
        }
        ready();
    })();
}

/*
    for other browsers, give up on the time saving
    techniques and wait for all the images to load.
    also, do this in other browsers just in case they missed
    the boat.
*/
if (window.onload) {
    /* if there's already an onload listener, call ready after it,
    preserving first-come-first-serve event observation */
    window.onload = (function (onload) {
        return function () {
            onload.call(this);
            ready();
        };
    })(window.onload);
} else {
    window.onload = ready;
}


/*
    also create modulesjs.evalGlobal such that eval has no access to internal
    variables of the module modulesjs, not even the argument names passed
    to evalGlobal.

    /me cackles maniacally.

    see `evalGlobalWith`.
*/

/* end of module enclosure */
})(function () {
    return eval(arguments[0]);
});

/**

    References
    ==========

    .. [#parseUri] http://stevenlevithan.com
       Steven Levithan.  Provides regular expressions for both loose and strict
       URL parsing.

*/

/*license

    Legal
    =======
    
    Chiron is a component of the Tale web-game project.
    
    See <credit.txt> for a complete list of
    contributions and their licenses.  All contributions are provided
    under permissive, non-viral licenses including MIT, BSD, Creative Commons
    Attribution 2.5, Public Domain, or Unrestricted.
    
    
    License
    =======
    
    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    
    MIT License
    -----------
    
    Permission is hereby granted, free of charge, to any person
    obtaining a copy of this software and associated documentation
    files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use,
    copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following
    conditions:
    
    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    OTHER DEALINGS IN THE SOFTWARE.

*/

